/*
 * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.greenrobot.greendao.database;

import org.greenrobot.greendao.DaoException;

import java.io.File;
import java.lang.reflect.Constructor;

import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.rdb.RdbOpenCallback;
import ohos.data.rdb.RdbStore;
import ohos.data.rdb.StoreConfig;
import ohos.data.resultset.ResultSetHook;

/**
 * SQLiteOpenHelper to allow working with greenDAO's {@link Database} abstraction to create and update database schemas.
 */
public abstract class DatabaseOpenHelper extends DatabaseHelper {
    private final Context context;
    private final String name;
    private final int version;
    private final ResultSetHook mCursorFactory;
    private EncryptedHelper encryptedHelper;
    private boolean loadSQLCipherNativeLibs = true;

    public DatabaseOpenHelper(Context context, String name, int version) {
        this(context, name, null, version);
    }

    public DatabaseOpenHelper(Context context, String name, ResultSetHook factory, int version) {
        super(context);
        this.context = context;
        this.name = name;
        this.version = version;
        this.mCursorFactory = factory;
    }

    public DatabaseOpenHelper(Context context, String name, ResultSetHook factory, int version,
                        RdbCallback errorHandler) {
        super(context);
        this.context = context;
        this.name = name;
        this.version = version;
        this.mCursorFactory = factory;
    }

    /**
     * Flag to load SQLCipher native libs (default: true).
     *
     * @param loadSQLCipherNativeLibs load nativelibs
     */
    public void setLoadSQLCipherNativeLibs(boolean loadSQLCipherNativeLibs) {
        this.loadSQLCipherNativeLibs = loadSQLCipherNativeLibs;
    }

    /**
     * Get writable database
     * @return database
     */
    public Database getWritableDb() {
        StoreConfig config = StoreConfig.newDefaultConfig(name);
        return wrap(getRdbStore(config, version, new RdbCallback(this), mCursorFactory));
    }

    /**
     * Get Readable Database
     * @return database
     */
    public Database getReadableDb() {
        StoreConfig config = StoreConfig.newReadOnlyConfig(name);
        return wrap(getRdbStore(config, version, new RdbCallback(this), mCursorFactory));
    }

    /**
     * Wrap database
     * @param rdbStore RdbStore
     * @return StandardDatabase
     */
    protected Database wrap(RdbStore rdbStore) {
        return new StandardDatabase(rdbStore);
    }

    /**
     * callback to create the database
     * @param db db to create
     */
    public void onCreate(Database db) {
        // Do nothing by default
    }

    /**
     * callabck for database upgrade
     * @param db db to upgrade
     * @param oldVersion old version
     * @param newVersion new version
     */
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        // Do nothing by default
    }

    /**
     * Callback to open the database
     * @param db database to open
     */
    public void onOpen(Database db) {
        // Do nothing by default
    }

    interface EncryptedHelper {
        Database getEncryptedReadableDb(String password);

        Database getEncryptedReadableDb(char[] password);

        Database getEncryptedWritableDb(String password);

        Database getEncryptedWritableDb(char[] password);
    }

    private EncryptedHelper checkEncryptedHelper() {
        if (encryptedHelper == null) {
            try {
                Class.forName("net.sqlcipher.database.SQLiteOpenHelper");
            } catch (ClassNotFoundException e) {
                throw new DaoException("Using an encrypted database requires SQLCipher, " +
                        "make sure to add it to dependencies");
            }

            // Avoid referencing SqlCipherEncryptedHelper to avoid
            // "Rejecting re-init on previously-failed class java.lang.NoClassDefFoundError" logs
            // if SQLCipher is not in classpath.
            try {
                Class<?> helperClass = Class.forName(
                        "org.greenrobot.greendao.database.SqlCipherEncryptedHelper");
                Constructor<?> constructor = helperClass.getConstructor(
                        DatabaseOpenHelper.class, Context.class, String.class, int.class, boolean.class);
                encryptedHelper = (EncryptedHelper) constructor.newInstance(
                        this, context, name, version, loadSQLCipherNativeLibs);
            } catch (Exception e) {
                throw new DaoException(e);
            }
        }
        return encryptedHelper;
    }

    /**
     * Get encrypted writable database
     * @param password password
     * @return database
     */
    public Database getEncryptedWritableDb(String password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.getEncryptedWritableDb(password);
    }

    /**
     * Get encrypted writable database
     * @param password password
     * @return database
     */
    public Database getEncryptedWritableDb(char[] password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.getEncryptedWritableDb(password);
    }

    /**
     * Get encrypted readable database
     * @param password password
     * @return database
     */
    public Database getEncryptedReadableDb(String password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.getEncryptedReadableDb(password);
    }

    /**
     * Get encrypted readable database
     * @param password password
     * @return database
     */
    public Database getEncryptedReadableDb(char[] password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.getEncryptedReadableDb(password);
    }

    private class RdbCallback extends RdbOpenCallback {
        DatabaseOpenHelper mHelper;

        public RdbCallback(DatabaseOpenHelper helper) {
            this.mHelper = helper;
        }

        @Override
        public void onCorruption(File databaseFile) {
        }

        @Override
        public void onCreate(RdbStore rdbStore) {
                mHelper.onCreate(wrap(rdbStore));
        }

        @Override
        public void onUpgrade(RdbStore rdbStore, int i1, int i2) {
            mHelper.onUpgrade(wrap(rdbStore), i1, i2);
        }

        @Override
        public void onOpen(RdbStore rdbStore) {
            mHelper.onOpen(wrap(rdbStore));
        }
    }

}
